#include "qabstracthost.h"

#include "../property/qabstractproperty.h"
#include "../qpropertyfactory.h"
#include "../xmlobject.h"
#include "../qhostfactory.h"
#include "../qlanguage.h"
#include "../qlanguagemanager.h"
#include "../qcommonstruct.h"
#include "../qdatamanager.h"
#include "../property/stylesheetitem/qabstractstylesheetitem.h"
#include "../property/stylesheetitem/qstylesheetitemfactory.h"
#include "../property/stylesheetitem/stylesheetstruct.h"
#include "../qdata.h"

#include <QMouseEvent>
#include <QFocusEvent>
#include <QPaintEvent>
#include <QContextMenuEvent>
#include <QTimer>
#include <QUuid>
#include <QMetaMethod>
#include <QScriptEngine>
#include <QGraphicsProxyWidget>

QAbstractHost::QAbstractHost(QAbstractHost *parent) :
    QObject(parent),
    m_parent(parent),
    m_object(NULL),
    m_timer(new QTimer(this)),
    m_data_manager(NULL)
{
    setProperty("title",OBJECT_TITLE);
    set_attribute("uuid",QUuid::createUuid().toString());

    setProperty("function_list",QStringList()<<"destroyed(QObject*)"<<"destroyed()"<<"deleteLater()"
                <<"exec(QString,QMap<QString,QString>)"<<"translateChanged(QString)"
                <<"languageChanged()");

    if(m_parent!=NULL)
    {
        m_parent->m_children.append(this);
    }
    connect(m_timer,SIGNAL(timeout()),this,SLOT(property_refresh()));
}

QAbstractHost::~QAbstractHost()
{
    clear();
    if(m_parent!=NULL)
    {
        m_parent->m_children.removeAll(this);
    }
    if(m_object!=NULL)
    {
        m_object->deleteLater();
    }
    delete m_timer;
}

void QAbstractHost::insert_children(const QList<int> &indexs, const QList<QAbstractHost *> &children)
{
    QList<int> ind;
    QList<QAbstractHost*> list;

    for(int i=0;i<children.size();i++)
    {
        QAbstractHost *h=children.at(i);
        if(h->m_parent!=NULL)
        {
            continue;
        }
        int index=indexs.at(i);
        if(index<0 || index>m_children.size())
        {
            index=m_children.size();
        }
        h->m_parent=this;
        if(h->get_object()->isWidgetType())
        {
            QWidget* w=(QWidget*)h->get_object();
            w->setParent((QWidget*)m_object);
            w->setVisible(true);
        }
        else
        {
            h->get_object()->setParent(m_object);
        }
        m_children.insert(index,h);
        h->set_page_manager(m_page_manager);
        h->set_language_manager(m_language_manager);
        h->set_data_manager(m_data_manager);
        h->set_resource_manager(m_resource_manager);
        ind.append(index);
        list.append(h);
    }
    emit insert_children_signal(list,ind);
}

void QAbstractHost::remove_children(const QList<QAbstractHost *> &children)
{
    QList<QAbstractHost*> list;

    foreach(QAbstractHost* h,children)
    {
        if(h->m_parent!=this)
        {
            continue;
        }
        list.append(h);
    }

    if(list.size()==0)
    {
        return;
    }
    emit remove_children_signal(list);

    foreach(QAbstractHost* h,list)
    {
        h->m_parent=NULL;
        if(h->m_object->isWidgetType())
        {
            QWidget* w=(QWidget*)h->m_object;
            w->setParent(NULL);
            w->setVisible(false);
        }
        else
        {
            h->m_object->setParent(NULL);
        }
        m_children.removeAll(h);
    }
}

QList<QAbstractHost*>   QAbstractHost::get_children()
{
    return m_children;
}

QAbstractHost* QAbstractHost::get_child(int index)
{
    if(index<0 || index>=m_children.size())
    {
        return NULL;
    }
    else
    {
        return m_children.at(index);
    }
}

QAbstractHost* QAbstractHost::get_parent()
{
    return m_parent;
}

int QAbstractHost::get_child_count()
{
    return m_children.size();
}

QAbstractProperty* QAbstractHost::get_property(const QString &name)
{
    QStringList list=name.split(".");
    if(list.size()>=0)
    {
        QAbstractProperty* pro=m_nameToProperty.value(list.takeFirst());
        while(list.size()>0)
        {
            pro=pro->get_child(list.takeFirst());
            if(pro==NULL)
            {
                break;
            }
        }
        return pro;
    }
    return NULL;
}

QList<QAbstractProperty*> QAbstractHost::get_propertys()
{
    return m_propertys;
}

QVariant QAbstractHost::get_property_value(const QString &name)
{
    QAbstractProperty *pro=get_property(name);
    if(pro!=NULL)
    {
        return pro->get_value();
    }
    else
    {
        return QVariant();
    }
}

void QAbstractHost::set_property_value(const QString &name, const QVariant &value)
{
    QAbstractProperty *pro=get_property(name);
    if(pro!=NULL)
    {
        pro->set_value(value);
        QAbstractProperty *p=pro;
        while(p->get_parent()!=NULL)
        {
            p=p->get_parent();
        }
        m_object->setProperty(p->get_property("name").toByteArray(),p->get_value());
        if(pro->get_attribute("group")=="Style Sheet")
        {
            make_stylesheet();
        }
        else if(pro->get_attribute("name")=="objectName")
        {
            setObjectName(value.toString());
        }
    }
}

void QAbstractHost::insert_property(QAbstractProperty *property, int index)
{
    if(m_propertys.contains(property))
    {
        return;
    }

    if(index<0 || index>=m_propertys.size())
    {
        index=m_propertys.size();
    }
    property->set_host(this);
    m_propertys.insert(index,property);
    m_nameToProperty.insert(property->get_property("name").toString(),property);

    QList<QAbstractProperty*> list;
    list.append(property);

    QAbstractProperty* pro;
    while(list.size()>0)
    {
        pro=list.takeFirst();
        connect(pro,SIGNAL(value_chaged(QVariant,QVariant)),this,SLOT(property_changed()));
        list+=pro->get_children();
    }
}

void QAbstractHost::remove_property(const QString &name)
{
    QAbstractProperty *pro=m_nameToProperty.value(name);
    m_nameToProperty.remove(name);
    if(pro!=NULL)
    {
        m_propertys.removeAll(pro);
        delete pro;
    }
}

void QAbstractHost::set_default()
{
    foreach(QAbstractProperty* pro,m_propertys)
    {
        pro->set_default();
        if(m_object!=NULL)
        {
            m_object->setProperty(pro->get_property("name").toByteArray(),pro->get_value());
        }
        make_stylesheet();
    }
    foreach(QAbstractHost* h,m_children)
    {
        h->set_default();
        h->make_stylesheet();
    }

    m_timer->start(50);
}

void QAbstractHost::insert_action(const QString &name, QAction *ac, int index)
{
    if(m_actions.contains(ac))
    {
        return;
    }

    if(index<0 || index>=m_actions.size())
    {
        index=m_actions.size();
    }

    m_actions.insert(index,ac);
    m_nameToAction.insert(name,ac);
}

void QAbstractHost::remove_action(const QString &name)
{
    QAction* ac=m_nameToAction.value(name);
    m_nameToAction.remove(name);
    if(ac!=NULL)
    {
        m_actions.removeAll(ac);
        delete ac;
    }
}

QAction* QAbstractHost::get_action(const QString &name)
{
    return m_nameToAction.value(name);
}

QList<QAction*> QAbstractHost::get_actions()
{
    return m_actions;
}

QObject* QAbstractHost::get_object()
{
    return m_object;
}

void QAbstractHost::set_attribute(const QString &key, const QString &value)
{
    m_attributes.insert(key,value);
}

QString QAbstractHost::get_attribute(const QString &key)
{
    return m_attributes.value(key);
}

void QAbstractHost::init_property()
{
    QAbstractProperty* pro;

    pro=QPropertyFactory::create_property("ByteArray");
    if(pro!=NULL)
    {
        pro->set_property("name","objectName");
        pro->set_attribute("show_name",tr("Name"));
        pro->set_attribute("group","Attributes");
        pro->set_attribute(ATTR_NOT_DATA,true);
        pro->set_attribute(ATTR_NEEDSAVE,true);
        insert_property(pro);
    }
}

void QAbstractHost::to_object(XMLObject *xml)
{
    if(xml!=NULL)
    {
        xml->clear();

        xml->set_title(property("title").toString());

        QMapIterator<QString,QString>       it(m_attributes);
        while(it.hasNext())
        {
            it.next();
            xml->set_property(it.key(),it.value());
        }

        XMLObject* obj;
        foreach(QAbstractProperty* pro,m_propertys)
        {
            if(pro->modified() || pro->get_attribute(ATTR_NEEDSAVE).toBool())
            {
                obj=new XMLObject;
                pro->toObject(obj);
                xml->inser_child(-1,obj);
            }
        }

        foreach(QAbstractHost* h,m_children)
        {
            obj=new XMLObject;
            h->to_object(obj);
            xml->inser_child(-1,obj);
        }
    }
}

void QAbstractHost::from_object(XMLObject *xml)
{
    if(xml!=NULL)
    {
        if(xml->get_title()!=property("title").toString())
        {
            return;
        }

        clear();

        QMapIterator<QString,QString>       it(m_attributes);
        while(it.hasNext())
        {
            it.next();
            m_attributes.insert(it.key(),xml->get_property(it.key()));
        }

        if(get_uuid()=="")
        {
            set_uuid(QUuid::createUuid().toString());
        }

        QList<XMLObject*>   children=xml->get_children();
        QString str;
        foreach(XMLObject* obj,children)
        {
            str=obj->get_title();
            QAbstractProperty *pro=m_nameToProperty.value(str);
            if(pro!=NULL)
            {
                pro->fromObject(obj);
                pro->set_attribute(ATTR_NEEDSAVE,true);
            }
            else
            {
                QString name=obj->get_property(HOST_TYPE);

                QAbstractHost *h=QHostFactory::create_host(name);
                if(h!=NULL)
                {
                    h->from_object(obj);
                    h->m_parent=this;
                    if(h->get_object()->isWidgetType())
                    {
                        QWidget* w=(QWidget*)h->get_object();
                        w->setParent((QWidget*)m_object);
                        w->setVisible(true);
                    }
                    else
                    {
                        h->get_object()->setParent(m_object);
                    }
                    m_children.append(h);
                }
            }
        }
        make_stylesheet();
    }
}

void QAbstractHost::clear()
{
    while(m_children.size()>0)
    {
        delete m_children.first();
    }
}

QString QAbstractHost::get_show_name()
{
    return "";
}

QString QAbstractHost::get_show_icon()
{
    return "";
}

QString QAbstractHost::get_show_group()
{
    return "";
}

void QAbstractHost::create_object()
{
}

void QAbstractHost::init()
{
    create_object();
    init_property();

    if(m_object!=NULL)
    {
        m_object->installEventFilter(this);
        foreach(QAbstractProperty* pro,m_propertys)
        {
            pro->set_value(m_object->property(pro->get_property("name").toByteArray()));
        }
    }

}

bool QAbstractHost::eventFilter(QObject *o, QEvent *e)
{
    if(o==m_object)
    {
        if(e->type()==QEvent::Paint)
        {
            return handle_paint_event((QPaintEvent*)e);
        }
        else if(e->type()==QEvent::MouseButtonPress)
        {
            return handle_mouse_press_event((QMouseEvent*)e);
        }
        else if(e->type()==QEvent::MouseButtonRelease)
        {
            return handle_mouse_release_event((QMouseEvent*)e);
        }
        else if(e->type()==QEvent::MouseMove)
        {
            return handle_mouse_move_event((QMouseEvent*)e);
        }
        else if(e->type()==QEvent::MouseButtonDblClick)
        {
            return handle_double_clicked_event((QMouseEvent*)e);
        }
        else if(e->type()==QEvent::FocusIn)
        {
            return handle_focus_in_event((QFocusEvent*)e);
        }
        else if(e->type()==QEvent::FocusOut)
        {
            return handle_focus_out_event((QFocusEvent*)e);
        }
        else if(e->type()==QEvent::ContextMenu)
        {
            return handle_context_menu_event((QContextMenuEvent*)e);
        }
    }
    return QObject::eventFilter(o,e);
}

bool QAbstractHost::handle_context_menu_event(QContextMenuEvent *)
{
    return false;
}

bool QAbstractHost::handle_paint_event(QPaintEvent *)
{
    return false;
}

bool QAbstractHost::handle_mouse_press_event(QMouseEvent *e)
{
    QString str=get_property_value("mousePress").toString();
    if(str!="")
    {
        QMap<QString,QVariant> param,ret;
        param.insert("x",e->x());
        param.insert("y",e->y());
        param.insert("button",(int)e->button());
        exec(str,param,ret);
    }
    return false;
}

bool QAbstractHost::handle_mouse_release_event(QMouseEvent *e)
{
    QString str=get_property_value("mouseRelease").toString();
    if(str!="")
    {
        QMap<QString,QVariant> param,ret;
        param.insert("x",e->x());
        param.insert("y",e->y());
        param.insert("button",(int)e->button());
        exec(str,param,ret);
        return true;
    }
    return false;
}

bool QAbstractHost::handle_mouse_move_event(QMouseEvent *e)
{
    QString str=get_property_value("mouseMove").toString();
    if(str!="")
    {
        QMap<QString,QVariant> param,ret;
        param.insert("x",e->x());
        param.insert("y",e->y());
        param.insert("button",(int)e->button());
        exec(str,param,ret);
    }
    return false;
}

bool QAbstractHost::handle_double_clicked_event(QMouseEvent *e)
{
    QString str=get_property_value("doubleClick").toString();
    if(str!="")
    {
        QMap<QString,QVariant> param,ret;
        param.insert("x",e->x());
        param.insert("y",e->y());
        param.insert("button",(int)e->button());
        exec(str,param,ret);
        return true;
    }
    return false;
}

bool QAbstractHost::handle_focus_in_event(QFocusEvent *)
{
    return false;
}

bool QAbstractHost::handle_focus_out_event(QFocusEvent *)
{
    return false;
}

void QAbstractHost::property_refresh()
{
    if(m_object==NULL)
    {
        return;
    }
    foreach(QAbstractProperty* pro,m_propertys)
    {
        QVariant v=m_object->property(pro->get_property("name").toByteArray());
        if(!pro->cmp(v))
        {
            pro->set_value(v);

            QVariant val=get_property_value("memery");
            if(val.isValid())
            {
                MemeryValueItems items=val.value<MemeryValueItems>();
                foreach(const tagMemeryValueInfo &info,items)
                {
                    if(info.m_propertyName==pro->get_name())
                    {
                        emit changed_data(info.m_uuid,pro->get_value());
                        //m_data_manager->set_value(info.m_uuid,pro->get_value());
                    }
                }
            }
        }
    }
}

QString QAbstractHost::get_uuid()
{
    return m_attributes.value("uuid");
}

void QAbstractHost::set_uuid(const QString &uuid)
{
    m_attributes.insert("uuid",uuid);
}

void QAbstractHost::set_language_manager(QLanguageManager *language)
{
    m_language_manager=language;
    connect(language,SIGNAL(current_language_changed()),this,SLOT(current_language_changed()));
    connect(language,SIGNAL(current_text_changed(QString)),this,SLOT(current_text_changed(QString)));
    foreach(QAbstractHost* host,m_children)
    {
        host->set_language_manager(language);
    }
    current_language_changed();
}

QLanguageManager* QAbstractHost::get_language_manager()
{
    return m_language_manager;
}

void QAbstractHost::current_language_changed()
{
    QLanguage* l=m_language_manager->get_current_language();
    foreach(QAbstractProperty* pro,m_propertys)
    {
        if(pro->get_property("tr").toBool())
        {
            tagTranslateInfo *info=NULL;
            if(l!=NULL)info=l->get_translate(pro->get_property("uuid").toString());
            if(info==NULL)
            {
                pro->get_host()->set_property_value(pro->get_property("name").toString(),"");
            }
            else
            {
                pro->get_host()->set_property_value(pro->get_property("name").toString(),info->m_translate);
            }
        }
    }
}

void QAbstractHost::current_text_changed(const QString &uuid)
{
    QLanguage* l=m_language_manager->get_current_language();
    foreach(QAbstractProperty* pro,m_propertys)
    {
        if(pro->get_property("uuid").toString()==uuid)
        {
            tagTranslateInfo *info=NULL;
            if(l!=NULL)info=l->get_translate(uuid);
            if(info==NULL)
            {
                pro->get_host()->set_property_value(pro->get_property("name").toString(),"");
            }
            else
            {
                pro->get_host()->set_property_value(pro->get_property("name").toString(),info->m_translate);
            }
            return;
        }
    }
}

QString QAbstractHost::functionInformation(const QString &name)
{
    int index=metaObject()->indexOfMethod(name.toLocal8Bit());
    if(index==-1)
    {
        return "";
    }
    QString ret;
    QMetaMethod method=this->metaObject()->method(index);

    QList<QByteArray> parameters=method.parameterNames();
    QString para;

    QAbstractProperty *pro;

    for(int i=0;i<parameters.size();i++)
    {
        para=parameters.at(i);
        ret+=(tr("Parameter ")+QString::number(i+1)+" : "+para+"\n");
        pro=m_nameToProperty.value(para,NULL);
        if(pro!=NULL)
        {
            if(pro->get_property("type")=="Enum")
            {
                ComboItems items=pro->get_attribute("items").value<ComboItems>();
                foreach(tagComboItem item,items)
                {
                    ret+=("    "+item.m_value.toString()+" = "+item.m_text+"\n");
                }
            }
            else if(pro->get_property("type")=="Cursor")
            {
                ComboItems items=pro->get_attribute("items").value<ComboItems>();
                foreach(tagComboItem item,items)
                {
                    QCursor c=item.m_value.value<QCursor>();
                    ret+=("    "+QString::number(c.shape())+"="+item.m_text+"\n");
                }
            }
        }

    }
    if(parameters.size()>0)
    {
        ret=ret.left(ret.size()-1);
    }
    return ret;
}

void QAbstractHost::set_page_manager(QPageManager *page)
{
    m_page_manager=page;
    foreach(QAbstractHost* h,m_children)
    {
        h->set_page_manager(page);
    }
}

void QAbstractHost::set_data_manager(QDataManager *data)
{
    m_data_manager=data;
    foreach(QAbstractHost* h,m_children)
    {
        h->set_data_manager(data);
    }
    connect(data,SIGNAL(data_value_changed(QString)),this,SLOT(data_value_changed(QString)));
}

void QAbstractHost::set_resource_manager(QResourceManager *resource)
{
    m_resource_manager=resource;
    foreach(QAbstractHost* h,m_children)
    {
        h->set_resource_manager(resource);
    }
}

void QAbstractHost::make_stylesheet()
{
    QString str;
    QString temp;
    QString head;
    foreach(QAbstractProperty* pro,m_propertys)
    {
        if(pro->inherits("QStylesheetProperty"))
        {
            QAbstractStylesheetItem *item=QStylesheetItemFactory::createItem(pro->get_property("name").toString());
            if(item!=NULL)
            {
                tagStylesheetItems list=pro->get_value().value<tagStylesheetItems>();
                foreach(tagStylesheetItem it,list)
                {
                    item->setValue(QVariant::fromValue<tagStylesheetItem>(it));
                    temp=item->makeStylesheet();
                    if(temp!="")
                    {
                        if(!it.m_attributes.value("no-head").toBool())
                        {
                            head="#"+m_object->property("objectName").toString();
                        }
                        if(it.m_attributes.value("title").toString()!="Normal")
                        {
                            head+=(it.m_attributes.value("title").toString());
                        }
                        temp=head+temp;
                    }
                    if(temp!="")
                    {
                        str+=temp;
                    }
                }
            }
        }
    }
    m_object->setProperty("styleSheet",str);
}

void QAbstractHost::set_parent(QAbstractHost *parent, int index)
{
    if(m_parent==parent)
    {
        return;
    }
    if(m_parent!=NULL)
    {
        m_parent->m_children.removeAll(this);
    }

    m_parent=parent;
    if(m_parent!=NULL)
    {
        if(index<0 || index>parent->m_children.size())
        {
            index=m_children.size();
        }
        m_parent->m_children.insert(index,this);
        if(m_object->isWidgetType())
        {
            QWidget* wid=(QWidget*)m_object;
            wid->setParent((QWidget*)parent->m_object);
            wid->setVisible(true);
        }
        else
        {
            m_object->setParent(parent->m_object);
        }
    }
    else
    {
        if(m_object->isWidgetType())
        {
            QWidget* wid=(QWidget*)m_object;
            wid->setParent(NULL);
        }
        else
        {
            m_object->setParent(NULL);
        }
    }
    emit parent_changed();
}

QString QAbstractHost::get_host_type()
{
    if(m_parent!=NULL)
    {
        return m_parent->get_host_type();
    }
    else
    {
        return property("title").toString();
    }
}

void QAbstractHost::update_memery_data()
{
    MemeryValueItems list=get_property_value("memery").value<MemeryValueItems>();
    foreach(tagMemeryValueInfo info,list)
    {
        tagDataInfo *data=m_data_manager->get_data(info.m_uuid);
        if(data!=NULL)
        {
            set_property_value(info.m_propertyName.toLocal8Bit(),data->m_value);
        }
    }
}

void QAbstractHost::property_changed()
{
    QAbstractProperty* pro=(QAbstractProperty*)sender();

    MemeryValueItems list=get_property_value("memery").value<MemeryValueItems>();
    foreach(tagMemeryValueInfo info,list)
    {
        if(info.m_propertyName==pro->get_name())
        {
            tagDataInfo *data=m_data_manager->get_data(info.m_uuid);
            if(data!=NULL)
            {
                emit changed_data(data->m_uuid,pro->get_value());
            }
        }
    }
}

void QAbstractHost::data_value_changed(const QString &uuid)
{
    tagDataInfo *data=m_data_manager->get_data(uuid);
    if(data!=NULL)
    {
        MemeryValueItems list=get_property_value("memery").value<MemeryValueItems>();
        foreach(tagMemeryValueInfo info,list)
        {
            if(info.m_uuid==uuid)
            {
                QAbstractProperty *pro=get_property(info.m_propertyName);
                if(pro!=NULL)
                {
                    emit changed_property(info.m_propertyName,data->m_value);
                }
            }
        }
    }
}
